package com.dsjy.yf.address.service.impl;

import com.dsjy.yf.address.component.MatchType;
import com.dsjy.yf.address.component.StringComparator;
import com.dsjy.yf.address.entity.SplitResult;
import com.dsjy.yf.address.entity.dataobject.*;
import com.dsjy.yf.address.service.*;
import com.dsjy.yf.address.utils.MyStringUtil;
import com.dsjy.yf.address.utils.RegularMatchUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.TreeSet;

/**
 * @author whj
 * @version 1.0
 * @date 2021-01-14 15:46
 */
@Service
public class AddressSplitServiceImpl implements AddressSplitService {
    public static Map<String, Integer> countyMap;
    public static Map<Integer, String> countyCodeMap;
    public static List<CountyDO> countyList;
    public static Map<Integer, String> townCodeMap;
    public static Map<String, Integer> townNameCodeMap;
    public static Map<Integer, List<TownDO>> townListMap;
    public static Map<Integer, List<CommunityDO>> communityListMap;


    @Autowired
    private ExclusivezoneService exclusivezoneService;
    @Autowired
    private TownService townService;
    @Autowired
    private CommunityService communityService;
    @Autowired
    private VillageService villageService;
    @Autowired
    private StreetService streetService;
    @Autowired
    private BuildingService buildingService;
    @Autowired
    private YardService yardService;
    @Autowired
    private SubareaService subareaService;
    @Autowired
    private YardRoadService yardRoadService;
    @Autowired
    private StringComparator stringComparator;
    @Autowired
    private PoiService poiService;


    @Override
    public SplitResult splitAddress(String address) {
        SplitResult result = RegularMatchUtil.splitToTown(address, countyMap.keySet());
        if (result.getRest() != null) {
            matchCounty(result);
        }
        if (result.getRest() != null) {
            matchExclusivezone(result);
        }
        if (result.getRest() != null) {
            matchTown(result);
        }
        if (result.getRest() != null) {
            matchStreet(result);
        }
        if (result.getRest() != null && result.getCommunity() == null) {
            matchCommunity(result);
        }
        if (result.getRest() != null) {
            matchVillage(result);
        }
        if (result.getRest() != null) {
            matchPoi(result);
        }
        if (result.getRest() != null) {
            matchBuilding(result);
        }
        if (result.getRest() != null) {
            YardDO yardDO = matchYard(result);
            if (result.getRest() != null && yardDO != null && yardDO.getIsHaveSub() == 1) {
                matchSubarea(result, yardDO.getId());
            }
            if (result.getRest() != null && yardDO != null && yardDO.getIsHaveRoad() == 1) {
                matchYardRoad(result, yardDO.getId());
            }
        }
        if (result.getRest() != null) {
            RegularMatchUtil.splitRest(result);
        }
        if (result.getRemark() != null && result.getRest() != null) {
            result.setRest(result.getRemark() + result.getRest());
        } else if (result.getRemark() != null) {
            result.setRest(result.getRemark());
        }
        return result;
    }


    /**
     * 匹配区县别名
     *
     * @param splitResult
     */
    public void matchCounty(SplitResult splitResult) {
        if (splitResult.getCounty() == null) {
            String rest = splitResult.getRest();
            for (CountyDO item : countyList) {
                String countyName = item.getCountyName();
                String alias = matchAlias(item.getCountyAlias(), rest, MatchType.STARTSWITH);
                if (alias != null) {
                    splitResult.setCounty(countyName);
                    splitResult.setCountyCode(item.getCountyCode());
                    updateRest(splitResult, alias);
                    break;
                }
            }
        } else {
            splitResult.setCountyCode(countyMap.get(splitResult.getCounty()));
        }
    }

    /**
     * 匹配类行政区划名
     *
     * @param splitResult
     */
    public void matchExclusivezone(SplitResult splitResult) {
        List<ExclusivezoneDO> list = null;
        if (splitResult.getCountyCode() == null) {
            list = exclusivezoneService.searchExclusivezoneAll();
        } else {
            list = exclusivezoneService.searchExclusivezoneWithParentCode(splitResult.getCountyCode(), true);
        }
        if (list == null || list.size() == 0) {
            return;
        }
        String rest = splitResult.getRest();
        for (ExclusivezoneDO item : list) {
            String name = item.getExclusivezoneName();
            if (name == null) {
                continue;
            }
            if (rest.contains(name)) {
                if (item.getIsAutonomy() != null) {
                    splitResult.setCounty(name);
                    splitResult.setCountyCode(countyMap.get(name));
                } else {
                    splitResult.setExclusivezone(name);
                }
                updateRest(splitResult, name);
            } else {
                String alias = matchAlias(item.getExclusivezoneAlias(), rest, MatchType.STARTSWITH);
                if (alias != null) {
                    if (item.getIsAutonomy() != null) {
                        splitResult.setCounty(name);
                        splitResult.setCountyCode(countyMap.get(name));
                    } else {
                        splitResult.setExclusivezone(name);
                    }
                    updateRest(splitResult, alias);
                }
            }
            if (splitResult.getExclusivezone() != null) {
                if (splitResult.getCounty() == null) {
                    splitResult.setCountyCode(item.getParentCode());
                    splitResult.setCounty(countyCodeMap.get(splitResult.getCountyCode()));
                }
                break;
            }
        }
    }

    /**
     * 匹配乡镇街道
     *
     * @param splitResult
     */
    public void matchTown(SplitResult splitResult) {
        List<TownDO> townNameList = null;
        if (splitResult.getCountyCode() != null) {
            townNameList = townListMap.get(splitResult.getCountyCode());
        } else {
            townNameList = townService.searchAll();
        }
        if (townNameList != null) {
            String rest = splitResult.getRest();
            for (TownDO item : townNameList) {
                String townName = item.getTownName();
                if (townName == null) {
                    continue;
                }
                if (rest.contains(townName)) {
                    splitResult.setTown(townName);
                    splitResult.setTownCode(item.getTownCode());
                    updateRest(splitResult, townName);
                    break;
                } else {
                    String alias = matchAlias(item.getTownAlias(), rest, MatchType.STARTSWITH);
                    if (alias != null) {
                        splitResult.setTown(townName);
                        splitResult.setTownCode(item.getTownCode());
                        updateRest(splitResult, alias);
                        break;
                    }
                }
            }
        }
        if (splitResult.getTown() == null) {
            RegularMatchUtil.splitTown(splitResult);
        }
        if (splitResult.getCounty() == null && splitResult.getTownCode() != null) {
            writeCountyWithTownCode(splitResult);
        }
    }

    /**
     * 匹配行政村社区
     *
     * @param splitResult
     */
    public void matchCommunity(SplitResult splitResult) {
        List<CommunityDO> communityDOList = null;
        boolean flag = false;
        Integer townCode = splitResult.getTownCode();
        communityDOList = townCode == null ? null : communityListMap.get(townCode);
        if (communityDOList == null) {
            Integer countyCode = splitResult.getCountyCode();
            communityDOList = countyCode == null ? null : communityService.searchWithTownCode(countyCode, true);
        }
        if (communityDOList == null && townCode == null && splitResult.getCountyCode() == null) {
            flag = true;
            communityDOList = communityService.searchCommunityOnly();
        }
        if (communityDOList != null) {
            String rest = splitResult.getRest();
            for (CommunityDO item : communityDOList) {
                String communityName = item.getCommunityName();
                if (communityName == null) {
                    continue;
                }
                if (rest.contains(communityName)) {
                    splitResult.setCommunity(communityName);
                    splitResult.setCommunityCode(item.getCommunityCode());
                    updateRest(splitResult, communityName);
                } else {
                    String alias = matchAlias(item.getCommunityAlias(), rest, MatchType.STARTSWITH);
                    if (alias != null) {
                        splitResult.setCommunity(communityName);
                        splitResult.setCommunityCode(item.getCommunityCode());
                        updateRest(splitResult, alias);
                    }
                }
                if (splitResult.getCommunity() != null) {
                    if (flag) {
                        Integer parentCode = item.getParentCode();
                        splitResult.setTownCode(parentCode);
                        splitResult.setTown(townService.searchByTownCode(parentCode));
                        writeCountyWithTownCode(splitResult);
                    }
                    break;
                }
            }
        }

    }

    /**
     * 匹配自然村
     *
     * @param splitResult
     */
    public void matchVillage(SplitResult splitResult) {
        Integer code = getMatchCode(splitResult);
        if (code != null) {
            List<VillageDO> villageList = villageService.searchVillageWithParentCode(code, true);
            String rest = splitResult.getRest();
            for (VillageDO item : villageList) {
                String villageName = item.getVillageName();
                if (villageName == null) {
                    continue;
                }
                if (rest.contains(villageName)) {
                    splitResult.setVillage(villageName);
                    updateRest(splitResult, villageName);
                    break;
                } else {
                    String alias = matchAlias(item.getVillageAlias(), rest, MatchType.STARTSWITH);
                    if (alias != null) {
                        splitResult.setVillage(villageName);
                        updateRest(splitResult, alias);
                        break;
                    }
                }
            }
        }
    }

    /**
     * 匹配兴趣点
     *
     * @param splitResult
     */
    public void matchPoi(SplitResult splitResult) {
        Integer code = splitResult.getCountyCode();
        if (code != null) {
            List<PoiDO> poiList = poiService.searchPoiWithParentCode(code, true);
            String rest = splitResult.getRest();
            for (PoiDO item : poiList) {
                String poiName = item.getPoiName();
                if (poiName == null) {
                    continue;
                }
                if (rest.contains(poiName)) {
                    splitResult.setPoi(poiName);
                    updateRest(splitResult, poiName);
                    break;
                } else {
                    String alias = matchAlias(item.getPoiAlias(), rest, MatchType.CONTAINS);
                    if (alias != null) {
                        splitResult.setPoi(poiName);
                        updateRest(splitResult, alias);
                        break;
                    }
                }
            }
        }
    }

    /**
     * 匹配街路巷弄名
     *
     * @param splitResult
     */
    public void matchStreet(SplitResult splitResult) {
        Integer code = splitResult.getCountyCode();
        if (code != null) {
            List<StreetDO> streetList = streetService.searchStreetWithParentCode(code, true);
            String rest = splitResult.getRest();
            for (StreetDO item : streetList) {
                String streetName = item.getStreetName();
                if (streetName == null) {
                    continue;
                }
                if (rest.contains(streetName)) {
                    splitResult.setStreet(streetName);
                    updateRest(splitResult, streetName);
                } else {
                    String alias = matchAlias(item.getStreetAlias(), rest, MatchType.CONTAINS);
                    if (alias != null) {
                        splitResult.setStreet(streetName);
                        updateRest(splitResult, alias);
                    }
                }
                if (splitResult.getStreet() != null && StringUtils.isNotEmpty(item.getCommunity()) && splitResult.getCommunity() == null) {
                    splitResult.setCommunity(item.getCommunity());
                    updateRest(splitResult, item.getCommunity());
                    break;
                } else if (splitResult.getStreet() != null) {
                    break;
                }
            }
        }
    }

    /**
     * 匹配建筑物
     *
     * @param splitResult
     */
    public void matchBuilding(SplitResult splitResult) {
        Integer code = splitResult.getCountyCode();
        if (code != null) {
            List<BuildingDO> buildingList = buildingService.searchBuildingWithCode(code, true);
            String rest = splitResult.getRest();
            for (BuildingDO item : buildingList) {
                String buildingName = item.getBuildingName();
                if (buildingName == null) {
                    continue;
                }
                if (rest.contains(buildingName)) {
                    splitResult.setBuildingName(buildingName);
                    updateRest(splitResult, buildingName);
                    break;
                } else {
                    String alias = matchAlias(item.getBuildingAlias(), rest, MatchType.CONTAINS);
                    if (alias != null) {
                        splitResult.setBuildingName(buildingName);
                        splitResult.setBuildingAlias(alias);
                        updateRest(splitResult, alias);
                        break;
                    }
                }
            }
        }
    }

    /**
     * 匹配院落名
     *
     * @param splitResult
     */
    public YardDO matchYard(SplitResult splitResult) {
        Integer code = splitResult.getCountyCode();
        if (code != null) {
            List<YardDO> yardDOList = yardService.searchYardWithCode(code, true);
            String rest = splitResult.getRest();
            for (YardDO item : yardDOList) {
                String yardName = item.getYardName();
                if (yardName == null) {
                    continue;
                }
                if (rest.contains(yardName)) {
                    splitResult.setYardName(yardName);
                    updateRest(splitResult, yardName);
                    return item;
                } else {
                    String alias = matchAlias(item.getYardAlias(), rest, MatchType.CONTAINS);
                    if (alias != null) {
                        splitResult.setYardName(yardName);
                        splitResult.setYardAlias(alias);
                        updateRest(splitResult, alias);
                        return item;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 匹配院落子区名
     *
     * @param splitResult
     */
    public void matchSubarea(SplitResult splitResult, Integer parentCode) {
        Integer code = getMatchCode(splitResult);
        if (code != null) {
            List<SubareaDO> subareaList = subareaService.searchSubareaWithParentCode(parentCode, true);
            String rest = splitResult.getRest();
            for (SubareaDO item : subareaList) {
                String subareaName = item.getSubareaName();
                if (subareaName == null) {
                    continue;
                }
                if (rest.contains(subareaName)) {
                    splitResult.setSubareaName(subareaName);
                    updateRest(splitResult, subareaName);
                    break;
                } else {
                    String alias = matchAlias(item.getSubareaAlias(), rest, MatchType.CONTAINS);
                    if (alias != null) {
                        splitResult.setSubareaName(subareaName);
                        splitResult.setSubareaAlias(alias);
                        updateRest(splitResult, alias);
                        break;
                    }
                }
            }
        }
    }

    /**
     * 匹配院落内道路
     *
     * @param splitResult
     */
    public void matchYardRoad(SplitResult splitResult, Integer parentCode) {
        Integer code = getMatchCode(splitResult);
        if (code != null) {
            List<YardRoadDO> yardRoadDOList = yardRoadService.searchYardRoadWithParentCode(parentCode, true);
            String rest = splitResult.getRest();
            for (YardRoadDO item : yardRoadDOList) {
                String road = item.getYardroadName();
                if (road != null && rest.contains(road)) {
                    splitResult.setInternalRoad(road);
                    updateRest(splitResult, road);
                    break;
                }
            }
        }
    }


    /**
     * 获取相对精确的区域码
     *
     * @param splitResult
     * @return
     */
    private Integer getMatchCode(SplitResult splitResult) {
        if (splitResult.getTownCode() != null) {
            return splitResult.getTownCode();
        } else if (splitResult.getCounty() != null) {
            return splitResult.getCountyCode();
        }
        return null;
    }

    /**
     * 通过乡镇街道code填充county,countyCode
     *
     * @param splitResult
     */
    private void writeCountyWithTownCode(SplitResult splitResult) {
        Integer townCode = splitResult.getTownCode();
        if (townCode != null) {
            splitResult.setCountyCode(townCode / 1000);
            splitResult.setCounty(countyCodeMap.get(splitResult.getCountyCode()));

        }
    }

    /**
     * 删除未匹配字符串中已匹配的词
     *
     * @param splitResult
     * @param target
     */
    private void updateRest(SplitResult splitResult, String target) {
        String rest = splitResult.getRest();
        if (rest != null && target != null) {
            rest = rest.replace(target, "");
            splitResult.setRest(rest.length() == 0 ? null : rest);
        }
    }

    /**
     * 匹配别名,有则返回,无则返回null
     *
     * @param alias
     * @param address
     * @return
     */
    private String matchAlias(String alias, String address, @NonNull MatchType type) {
        alias = MyStringUtil.emptyToNull(alias);
        if (alias == null || address == null) {
            return null;
        }
        TreeSet<String> aliasSet = getAliasSet(alias);
        if (aliasSet.size() > 0) {
            if (type == MatchType.CONTAINS) {
                for (String item : aliasSet) {
                    if (address.contains(item)) {
                        return item;
                    }
                }
            } else if (type == MatchType.STARTSWITH) {
                for (String item : aliasSet) {
                    if (address.startsWith(item)) {
                        return item;
                    }
                }
            } else {
                for (String item : aliasSet) {
                    if (address.endsWith(item)) {
                        return item;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 将别名字段值拆成TreeSet集合
     *
     * @param alias
     * @return
     */
    private TreeSet<String> getAliasSet(String alias) {
        String[] aliasArray = alias.split("/");
        TreeSet<String> treeSet = new TreeSet<>(stringComparator);
        for (String item : aliasArray) {
            if (item != null && item.length() > 0) {
                treeSet.add(item);
            }
        }
        return treeSet;
    }

}
